package org.chartsy.fractaldimension;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.text.DecimalFormat;
import java.util.LinkedHashMap;
import org.chartsy.main.ChartFrame;
import org.chartsy.main.chart.Indicator;
import org.chartsy.main.data.DataItem;
import org.chartsy.main.data.Dataset;
import org.chartsy.main.utils.DefaultPainter;
import org.chartsy.main.utils.Range;
import org.chartsy.main.utils.SerialVersion;
import org.openide.nodes.AbstractNode;
/**
*
* @author viorel.gheba
*/
public class FractalDimension
extends Indicator
{
private static final long serialVersionUID = SerialVersion.APPVERSION;
public static final String FD = "fd";
private IndicatorProperties properties;
public FractalDimension()
{
super();
properties = new IndicatorProperties();
}
public String getName()
{ return "Fractal Dimension"; }
public String getLabel()
{ return properties.getLabel() + " (" + properties.getPeriod() + ")"; }
public String getPaintedLabel(ChartFrame cf)
{ return getLabel(); }
public Indicator newInstance()
{ return new FractalDimension(); }
public LinkedHashMap getHTML(ChartFrame cf, int i)
{
LinkedHashMap ht = new LinkedHashMap();
DecimalFormat df = new DecimalFormat("#,##0.00");
double[] values = getValues(cf, i);
String[] labels = {"FD:"};
ht.put(getLabel(), " ");
if (values.length > 0)
{
Color[] colors = getColors();
for (int j = 0; j < values.length; j++)
{
ht.put(getFontHTML(colors[j], labels[j]),
getFontHTML(colors[j], df.format(values[j])));
}
}
return ht;
}
@Override
public Range getRange(ChartFrame cf)
{
Range range = super.getRange(cf);
range = new Range(Math.min(1.3d, range.getLowerBound()), Math.max(1.7d, range.getUpperBound()));
return range;
}
public void paint(Graphics2D g, ChartFrame cf, Rectangle bounds)
{
Dataset d = visibleDataset(cf, FD);
if (d != null)
{
if (maximized)
{
Range range = getRange(cf);
DefaultPainter.line(g, cf, range, bounds, d, properties.getColor(), properties.getStroke()); // paint line
if (properties.getInsideVisibility())
paintFill(g, cf, d, bounds, range, properties.getInsideTransparentColor(), 1.4d, 1.6d, true); // paint fill
}
}
}
public void calculate()
{
Dataset initial = getDataset();
if (initial != null && !initial.isEmpty())
{
int period = properties.getPeriod();
Dataset d = getDataset(initial, period);
addDataset(FD, d);
}
}
public boolean hasZeroLine()
{ return false; }
public boolean getZeroLineVisibility()
{ return false; }
public Color getZeroLineColor()
{ return null; }
public Stroke getZeroLineStroke()
{ return null; }
public boolean hasDelimiters()
{ return true; }
public boolean getDelimitersVisibility()
{ return true; }
public double[] getDelimitersValues()
{ return new double[] {1.4d, 1.6d}; }
public Color getDelimitersColor()
{ return properties.getDelimiterColor(); }
public Stroke getDelimitersStroke()
{ return properties.getDelimiterStroke(); }
public Color[] getColors()
{ return new Color[] {properties.getColor()}; }
public double[] getValues(ChartFrame cf)
{
Dataset d = visibleDataset(cf, FD);
int i = d.getLastIndex();
if (d.getDataItem(i) != null)
return new double[] {d.getCloseAt(i)};
return new double[] {0};
}
public double[] getValues(ChartFrame cf, int i)
{
Dataset d = visibleDataset(cf, FD);
if (d.getDataItem(i) != null)
return new double[] {d.getCloseAt(i)};
return new double[] {0};
}
public boolean getMarkerVisibility()
{ return properties.getMarker(); }
public AbstractNode getNode()
{ return new IndicatorNode(properties); }
@Override
public Double[] getPriceValues(ChartFrame cf)
{ return new Double[] {new Double(0.2), new Double(0.4), new Double(0.6), new Double(0.8), new Double(1), new Double(1.2), new Double(1.4), new Double(1.6), new Double(1.8), new Double(2.0)}; }
private Dataset getDataset(final Dataset initial, final int period) {
int T = (int)(period/2);
int count = initial.getItemsCount();
double[] input = new double[count];
for (int i = 0; i < count; i++)
input[i] = (initial.getHighAt(i) + initial.getLowAt(i)) / 2;
double[] smooth = new double[count];
for (int i = 0; i < count; i++)
smooth[i] = 0;
for (int i = 3; i < count; i++)
smooth[i] = (input[i] + 2*input[i-1] + 2*input[i-2] + input[i-3]) / 6;
double[] n1 = new double[count];
double[] n2 = new double[count];
double[] n3 = new double[count];
for (int i = 0; i < count; i++) {
n1[i] = 0;
n2[i] = 0;
n3[i] = 0;
}
for (int i = 2*T + 3; i < count; i++) {
n1[i] = (max(smooth, i, T) - min(smooth, i, T)) / T;
n2[i] = (max(smooth, i - T, T) - min(smooth, i - T, T)) / T;
n3[i] = (max(smooth, i, 2*T) - min(smooth, i, 2*T)) / (2*T);
}
double[] dimen = new double[count];
double[] ratio = new double[count];
for (int i = 0; i < count; i++) {
ratio[i] = 0;
dimen[i] = 0;
}
for (int i = 2*T + 23; i < count; i++) {
if (n1[i] > 0.0 && n2[i] > 0.0 && n3[i] > 0.0)
ratio[i] = ((Math.log(n1[i] + n2[i]) - Math.log(n3[i])) / Math.log(2) + dimen[i-1]) / 2f;
dimen[i] = avg(ratio, i, 20);
}
Dataset d = Dataset.EMPTY(count);
for (int i = 0; i < count; i++)
d.setDataItem(i, (dimen[i] == 0 ? null : new DataItem(initial.getTimeAt(i), dimen[i])));
return d;
}
private double avg(double[] list, int current, int period) {
double result = 0;
for (int i = current; i > current - period; i--) {
result += list[i];
}
return result/(double)period;
}
private double max(double[] list, int current, int period) {
double result = list[current];
for (int i = current; i > current - period; i--) {
if (result < list[i]) {
result = list[i];
}
}
return result;
}
private double min(double[] list, int current, int period) {
double result = list[current];
for (int i = current; i > current - period; i--) {
if (result > list[i]) {
result = list[i];
}
}
return result;
}
}